/*
 * ShMem.cpp
 *
 *  Created on: Nov 6, 2013
 *      Author: vladimir
 */

#include "ShMem.hpp"

#include "AccessType.hpp"
#include "ErrorSystemCall.hpp"

#include <utility>
#include <cerrno>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>


namespace vjipc
{

int JustOpenShmAndCheck( const std::string & name,
                         const int flags,
                         const mode_t mode )
{
	const int fd = shm_open( name.c_str(), flags, mode );

	if (-1 == fd )
	{
		const std::string errMsg( "Error opening or creating a shared memory (" + name + ")." );
		throw btexc::ErrorSystemCall( errMsg, errno );
	}

	return fd;
}

void ResizeShMemAfterCreate( const int fd,
                             const std::string & name,
                             const unsigned int length )
{
	if ( -1 == ftruncate( fd, length ) )
	{
		const std::string errMsg(
				"Error extending the size of a shared memory (" +  name + ") "
				"to (" + std::to_string( length ) + ")." );
		throw btexc::ErrorSystemCall( errMsg, errno );
	}
}

int CreateAccessToShMem( const vjipc::AccessType & accessType,
                         const std::string & name,
                         const unsigned int length )
{
	int fd = 0;
	switch ( accessType )
	{
		case vjipc::AccessType::READ_ONLY :
			fd = JustOpenShmAndCheck( name, O_RDONLY, 0 );
			break;

		case vjipc::AccessType::READ_WRITE :
			fd = JustOpenShmAndCheck( name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
			ResizeShMemAfterCreate( fd, name, length );
			break;

		default :
			throw btexc::ErrorBase( "Unsupported type " + std::to_string( static_cast<int>( accessType ) ) );
	}

	return fd;
}

ShMem::ShMem( const vjipc::AccessType & accessType,
              const std::string & name_,
              const unsigned int lenght ) :
            	name( name_ ),
				shMemDesc( CreateAccessToShMem( accessType, name_, lenght ) )
{
}

ShMem::~ShMem()
{
	if ( -1 != shMemDesc )
	{
		shm_unlink( name.c_str() );
	}
}

ShMem::ShMem( ShMem&& rhs ) :
				name( std::move( rhs.name ) ),
				shMemDesc( std::move( rhs.shMemDesc ) )
{
	rhs.shMemDesc = -1;
}

ShMem& ShMem::operator=( ShMem&& rhs )
{
	name = std::move( rhs.name );
	shMemDesc = std::move( rhs.shMemDesc );

	rhs.shMemDesc = -1;

	return *this;
}

int ShMem::GetFileDescriptor() const
{
	return shMemDesc;
}

} /* namespace vjipc */
